package concurrency;

import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

class TellerManager implements Runnable {
    private ExecutorService mExecutorService;

    private CustomerLine mCustomers;

    private PriorityQueue<Teller> mWorkingTellers = new PriorityQueue<Teller>();

    private Queue<Teller> mTellersDoingOtherThings = new LinkedList<Teller>();

    private int mAdjustmentPeriod;

    public TellerManager(ExecutorService e, CustomerLine customers, int adjustmentPeriod) {
        mExecutorService = e;
        mCustomers = customers;
        mAdjustmentPeriod = adjustmentPeriod;
        Teller teller = new Teller(customers);
        mExecutorService.execute(teller);
        mWorkingTellers.add(teller);
    }

    /**
     * Give a teller a different job or a break:
     */
    private void reassignOneTeller() {
        Teller teller = mWorkingTellers.poll();
        teller.doSomethingElse();
        mTellersDoingOtherThings.offer(teller);
    }

    private void adjustTellerNumber() {
        // This is actually a control system. By adjusting
        // the numbers, you can reveal stability issues in
        // the control mechanism.
        // If line is too long, add another teller:
        if (mCustomers.size() / mWorkingTellers.size() > 2) {
            // If tellers are on break or doing
            // another job, bring one back:
            if (mTellersDoingOtherThings.size() > 0) {
                Teller teller = mTellersDoingOtherThings.remove();
                teller.serveCustomerLine();
                mWorkingTellers.offer(teller);
                return;
            }
            // Else create (hire) a new teller
            Teller teller = new Teller(mCustomers);
            mExecutorService.execute(teller);
            mWorkingTellers.add(teller);
            return;
        }

        // If line is short enough, remove a teller:
        if (mWorkingTellers.size() > 1 && mCustomers.size() / mWorkingTellers.size() < 2) {
            reassignOneTeller();
        }

        // If there is no line, we only need one teller:
        if (mCustomers.size() == 0) {
            while (mWorkingTellers.size() > 1) {
                reassignOneTeller();
            }
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(mAdjustmentPeriod);
                adjustTellerNumber();
                System.out.print(mCustomers + " { ");
                for(Teller teller : mWorkingTellers) {
                    System.out.print(teller.shortString() + " ");
                }
                System.out.println("}");
            }
        } catch (InterruptedException e) {
            System.out.println(this + "interrupted");
            // e.printStackTrace();
        }
        System.out.println(this + "terminating");
    }

    @Override
    public String toString() {
        return "TellerManager ";
    }
}
